Vuetify + VeeValidate でダイアログを使った際のレイアウト崩れへの対処
前回に引き続き VeeValidate ネタです。
現象
以下のような感じで <v-dialog>
に scrollable
を設定した上で、<ValidationObserver>
と <v-form>
<v-card>
を組み合わせると、
ダイアログのレイアウトが崩れます。
<v-dialog v-model="dialog" scrollable max-width="300px"> : : <ValidationObserver v-slot="{ invalid }" ref="obs" immediate> <v-form> <v-card> <v-card-title>Select Country</v-card-title> <v-divider></v-divider> <v-card-text style="height: 300px;"> <ValidationProvider v-slot="{ errors, valid }" name="country" rules="required"> <v-radio-group v-model="country" column :error-messages="errors" :success="valid"> <v-radio label="Bahamas, The" value="bahamas"></v-radio> <v-radio label="Bahrain" value="bahrain"></v-radio> : : </v-radio-group> </ValidationProvider> </v-card-text> <v-divider></v-divider> <v-card-actions> <v-btn color="blue darken-1" text @click="dialog = false">Close</v-btn> <v-btn color="blue darken-1" text @click="dialog = false" :disabled="invalid">Save</v-btn> </v-card-actions> </v-card> </v-form> </ValidationObserver> </v-dialog>
VeeValidate 適用前:
VeeValidate 適用後:
環境情報
- Node.js: 12.18.2
- Vue.js: 2.6.11
- Vuetify: 2.3.3
- VeeValidate: 3.3.7
- Vue CLI: 4.4.6
原因
issues にも上がっていましたが、あらゆるパターンに対応しているわけではないので、その場合は自分で実装すべき、という回答でクローズされていました。
See #3713. It isn't realistic to support every possible combination, you'll have to implement it yourself.
HTMLを確認したところ、VeeValidate の <ValidationObserver>
が発行する <span>
が原因で、Vuetify が定義している CSS にマッチしなくなっていました。
対処方法1
VuetifyのCSSは、以下のように .v-dialog--scrollable
と <form>
または .v-card
が親子関係である前提となっています。
.v-dialog--scrollable, .v-dialog--scrollable > form display: flex > .v-card display: flex flex: 1 1 100% flex-direction: column max-height: 100% max-width: 100% > .v-card__title, > .v-card__actions flex: 0 0 auto > .v-card__text backface-visibility: hidden flex: 1 1 auto overflow-y: auto
上記の <span>
はこの間に挟まれる構造のため、scrollable
関係の CSS が効かなくなっていました。
そこで、親子ではなく子孫関係でも適用されるような別CSSを書いて適用することで、レイアウト崩れはなくなりました。
対処方法1の問題点
レイアウト崩れはなくなったのですが、Vuetify 標準とは別のCSSを定義したことにより将来的な変更に追従できなくなる、というリスクがあります。
そのため、保守性を重視したいシステムの場合はあまり取りたくない方法です。
対処方法2
方法1で最低限の保険は掛けた状態ですが、他に方法はないかと考え、改めて VeeValidate のドキュメントを確認したところ、なんとそのまんまの情報がありました。
A slim prop can be used to force the component to be renderless, by default it is set to false.
<ValidationObserver>
に slim
を適用するだけで、レイアウト崩れはなくなりました。
まずは一次ソースに当たれ、という原則を改めて思い知ることになりました…(Vuetify側でなんとかすべきと考えて、VeeValidate 側で対処されているという想像に至らなかった)。
例によって、上記確認出来るソースを以下リポジトリに置いています。